package com.jichangxiu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Dynamic;
import com.jichangxiu.common.annotation.Scope;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.system.entity.bo.SysDataBo;
import com.jichangxiu.system.entity.dto.add.AddSysData;
import com.jichangxiu.system.entity.dto.edit.EditSysData;
import com.jichangxiu.system.entity.dto.query.QuerySysData;
import com.jichangxiu.system.entity.po.SysData;
import com.jichangxiu.system.entity.po.SysType;
import com.jichangxiu.system.entity.vo.SysDataVo;
import com.jichangxiu.system.mapper.SysDataMapper;
import com.jichangxiu.system.service.SysDataService;
import com.jichangxiu.system.service.SysTypeService;
import com.jichangxiu.common.entity.bo.ValidateBos;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.ValidateUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * SysDataServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:19
 */
@Service
public class SysDataServiceImpl extends BaseServiceImpl<SysDataMapper, SysData, SysDataVo> implements SysDataService {

    @Resource
    private SysTypeService sysTypeService;

    /**
     * 新增系统数据
     *
     * @param addSysData 新增系统数据视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddSysData addSysData) {
        return getBaseMapper().insert(addAndEditValidBo(ConvertUtils.convert(addSysData, SysDataBo.class))) > 0;
    }

    /**
     * 单（多）选删除系统数据
     *
     * @param idList 系统数据主键列表
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(List<String> idList) {
        return getBaseMapper().deleteBatchIds(delValid(idList)) > 0;
    }

    /**
     * 修改系统数据
     *
     * @param editSysData 修改系统数据视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysData editSysData) {
        return getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysData, SysDataBo.class))) > 0;
    }

    /**
     * 根据主键查询系统数据
     *
     * @param id       系统数据主键
     * @param isExtend 是否扩展
     * @return 系统数据详情
     */
    @Dynamic
    @Override
    public SysDataVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysDataVo sysDataVo = getBaseMapper().selectJoinOne(SysDataVo.class, buildQueryWrapper(QuerySysData.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysDataVo))
            sysDataVo = renderSysDataVoList(ListUtil.toList(sysDataVo)).get(0);
        return sysDataVo;
    }

    /**
     * （可分页）查询系统数据列表
     *
     * @param querySysData 查询视图对象
     * @return （分页）系统数据列表
     */
    @Dynamic
    @Override
    public Page<SysDataVo> lists(QuerySysData querySysData) {
        Page<SysDataVo> sysDataVoPage = getBaseMapper().selectJoinPage(startPage(), SysDataVo.class, buildQueryWrapper(querySysData));
        if (Boolean.TRUE.equals(querySysData.getIsExtend()))
            sysDataVoPage.setRecords(renderSysDataVoList(sysDataVoPage.getRecords()));
        return sysDataVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysDataBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysData addAndEditValidBo(SysDataBo sysDataBo) {
        SysData sysData = ConvertUtils.convert(sysDataBo, SysData.class);
        if (ObjectUtil.isNull(sysData))
            throw new ServiceException("待校验对象为空");
        if (StrUtil.isEmpty(sysData.getTypeId()))
            throw new ServiceException("没有指定系统类型");
        SysType sysType = sysTypeService.getById(sysData.getTypeId());
        if (sysData.getDefaulted()
                && count(new LambdaQueryWrapper<SysData>()
                .ne(StrUtil.isNotEmpty(sysData.getId()), SysData::getId, sysData.getId())
                .eq(SysData::getDefaulted, true)
                .in(SysData::getTypeId, sysData.getTypeId())) > 0)
            throw new ServiceException("【" + sysType.getName() + "】该字典类型的字典值已经存在一个默认值了");
        List<ValidateBos> validateBosList = Stream.of(
                ValidateUtils.createNeValidateBos(ValidateUtils.createValidateBo(SysData::getId, sysData.getId(), "主键")),
                ValidateUtils.createOrEqValidateBos(
                        ValidateUtils.createValidateBo(SysData::getKey, sysData.getKey(), "键名"),
                        ValidateUtils.createValidateBo(SysData::getValue, sysData.getValue(), "键值"),
                        ValidateUtils.createValidateBo(SysData::getRemark, sysData.getRemark(), "备注"))
        ).collect(Collectors.toList());
        // 唯一校验
        ValidateUtils.uniqueValidate(this::countWrapper, validateBosList);
        if (StrUtil.isEmpty(sysData.getId())) {
            // 新增
            return sysData;
        } else {
            // 修改
            SysData dbSysData = getById(sysData.getId());
            if (!dbSysData.getTypeId().equals(sysData.getTypeId()))
                throw new ServiceException(sysData.getKey() + "该字典值不能修改字典类型ID");
            BeanUtil.copyProperties(sysData, dbSysData, true);
            return dbSysData;
        }
    }

    /**
     * 删除前校验
     *
     * @param idList 主键列表
     * @return 主键列表
     */
    private List<String> delValid(List<String> idList) {
        if (ObjectUtil.isEmpty(idList))
            throw new ServiceException("待校验主键列表为空");
        return idList;
    }

    /**
     * 渲染视图
     *
     * @param sysDataVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysDataVo> renderSysDataVoList(List<SysDataVo> sysDataVoList) {
        if (ObjectUtil.isNotEmpty(sysDataVoList)) {
            // todo 扩展信息填入
        }
        return sysDataVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysData 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysData> buildQueryWrapper(QuerySysData querySysData) {
        MPJLambdaWrapper<SysData> lambdaQueryWrapper = new MPJLambdaWrapper<SysData>().selectAll(SysData.class);
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysData.getId()), SysData::getId, querySysData.getId());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysData.getKey()), SysData::getKey, querySysData.getKey());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysData.getValue()), SysData::getValue, querySysData.getValue());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysData.getDisplay()), SysData::getDisplay, querySysData.getDisplay());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysData.getDefaulted()), SysData::getDefaulted, querySysData.getDefaulted());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysData.getTypeId()), SysData::getTypeId, querySysData.getTypeId());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysData.getSort()), SysData::getSort, querySysData.getSort());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysData.getRemark()), SysData::getRemark, querySysData.getRemark());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysData.getEnabled()), SysData::getEnabled, querySysData.getEnabled());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysData.getEnabledBy()), SysData::getEnabledBy, querySysData.getEnabledBy());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysData.getEnabledTime()), SysData::getEnabledTime, querySysData.getEnabledTime());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysData.getScope()), querySysData.getScope());
        lambdaQueryWrapper.orderByAsc(SysData::getSort);
        return lambdaQueryWrapper;
    }

}